﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace IndexedList
{
    public class FieldValueResolver : ExpressionVisitor
    {
        Dictionary<string, object> Fields = new Dictionary<string, object>();
        bool FindMember = false;
        bool FindValue = false;

        string membername;
        object membervalue;

        public bool IgnoreNulls
        {
            get;
            set;
        }

        public Dictionary<string, object> GetFieldValues(LambdaExpression expression)
        {
            Fields.Clear();
            Visit(expression);
            return Fields;
        }

        public override Expression Visit(Expression node)
        {
            return base.Visit(node);
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (FindMember)
            {
                if (node.Expression.NodeType == ExpressionType.Parameter)
                {
                    membername = node.Member.Name;
                    return node;
                }
            }
            else if (FindValue)
            {
                membervalue = getvalueformexpression(node);
                return node;
            }
            return base.VisitMember(node);
        }

        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node.NodeType == ExpressionType.Equal)
            {
                FindMember = true;
                this.Visit(node.Left);
                FindMember = false;


                if (node.Right.NodeType != ExpressionType.MemberAccess)
                {
                    membervalue = Expression.Lambda(node.Right).Compile().DynamicInvoke(null);
                }
                else
                {
                    FindValue = true;
                    this.Visit(node.Right);
                    FindValue = false;
                }

                if (IgnoreNulls && membervalue == null)
                {
                    //bo nothing
                }
                else
                {
                    Fields.Add(membername, membervalue);
                }

                return node;
            }
            return base.VisitBinary(node);
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (FindValue)
            {
                membervalue = node.Value;
                return node;
            }
            return base.VisitConstant(node);
        }

        object getvalueformexpression(MemberExpression node)
        {
            if (node.Expression == null)
            {
                if (node.Member.MemberType == MemberTypes.Field)
                {
                    return ((FieldInfo)node.Member).GetValue(null);
                }
                else
                {
                    return ((PropertyInfo)node.Member).GetValue(null, null);
                }
            }
            else if (node.Expression.NodeType == ExpressionType.Constant)
            {
                if (node.Member.MemberType == MemberTypes.Field)
                {
                    return ((FieldInfo)node.Member).GetValue(((ConstantExpression)node.Expression).Value);
                }
                else
                {
                    return ((PropertyInfo)node.Member).GetValue(((ConstantExpression)node.Expression).Value, null);
                }
            }
            else
            {
                object dd = getvalueformexpression((MemberExpression)node.Expression);
                if (node.Member.MemberType == MemberTypes.Field)
                {
                    return ((FieldInfo)node.Member).GetValue(dd);
                }
                else
                {
                    return ((PropertyInfo)node.Member).GetValue(dd, null);
                }
            }
        }
    }
}
